package com.xsomnus.Distributed_Architecture.lb.impl;

import com.xsomnus.Distributed_Architecture.lb.AbstractLoadBalancer;
import com.xsomnus.Distributed_Architecture.lb.Node;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author @xsomnus666_xiawenye★
 * @since 2019/7/19 0019 10:54
 * - 才需学也,学需静也/非淡泊无以明志，非宁静无以致远
 * <desc>
 * RandomLoadBalance 是加权随机算法的具体实现，它的算法思想很简单。
 * 假设我们有一组服务器 servers = [A, B, C]，
 * 他们对应的权重为 weights = [5, 3, 2]，权重总和为10。现在把这些权重值平铺在一维坐标值上，[0, 5) 区间属于服务器 A，[5, 8) 区间属于服务器 B，[8, 10) 区间属于服务器 C。接下来通过随机数生成器生成一个范围在 [0, 10) 之间的随机数，然后计算这个随机数会落到哪个区间上。比如数字3会落到服务器 A 对应的区间上，此时返回服务器 A 即可。权重越大的机器，在坐标轴上对应的区间范围就越大，因此随机数生成器生成的数字就会有更大的概率落到此区间内。只要随机数生成器产生的随机数分布性很好，在经过多次选择后，每个服务器被选中的次数比例接近其权重比例。比如，经过一万次选择后，服务器 A 被选中的次数大约为5000次，服务器 B 被选中的次数约为3000次，服务器 C 被选中的次数约为2000次。
 * </desc>
 */
public class RandomLoadBalancer extends AbstractLoadBalancer {


    protected Node doSelect(List<Node> invokers, String url) {
        int sum = invokers.stream().mapToInt(Node::getWeight).sum();
        // 提示: jdk自身的随机数效率不高，可以在启动java程序类携带-Djava.security.egd=file:/dev/./urandom使用linux系统的随机数
        int target = ThreadLocalRandom.current().nextInt(sum);

        for (Node node : invokers) {
            if (target < node.getWeight()) {
                return node;
            }
            target -= node.getWeight();
        }
        return null;
    }
}
